package br.senai.sc.controller.business;

import java.math.BigDecimal;

import br.senai.sc.controller.exception.BusinessException;

/**
 * Classe que contém os cálculos de IQA para o parâmetro Ph.
 * 
 * @author Tiago
 */
public final class CalculoPhBusiness {

	/**
	 * Peso fixo utilizado para o cálculo da qualidade do Ph (qi^w)
	 */
	private static final double PESO = 0.12;

	/**
	 * Define o intervalo para o cálculo dos coliformes fecais. De 0 a menos que
	 * 2.
	 */
	public static final int[] PRIMEIRO_INTERVALO = { 0, 2 };

	/**
	 * Define o intervalo para o cálculo dos coliformes fecais. De 2 a menos que
	 * 6.
	 */
	public static final int[] SEGUNDO_INTERVALO = { 2, 6 };

	/**
	 * Define o intervalo para o cálculo dos coliformes fecais. De 6 a menos que
	 * 9.
	 */
	public static final int[] TERCEIRO_INTERVALO = { 6, 9 };

	/**
	 * Define o intervalo para o cálculo dos coliformes fecais. De 9 a 12.
	 */
	public static final int[] QUARTO_INTERVALO = { 9, 12 };

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * retorno do calculo de cada intervalo de valor.
	 */
	public static final int ESCALA = 10;

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * cálculo de exponenciais.
	 */
	private static final int ESCALA_EXP = 20;

	private CalculoPhBusiness() {
	}

	/**
	 * Utilizado para fazer o cálculo do Ph caso o valor informado é maior ou
	 * igual a 2 e menor que 6. Obs: este método não considera o valor 6, o
	 * valor deve ser menor que 6.
	 * 
	 * Cálculo realizado: -0,291666666667652*[valor]^4 +
	 * 5,75000000000546*[valor]^3 - 34,7083333333449*[valor]^2 +
	 * 85,2500000001091*[valor] - 71,0000000003465
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto.
	 */
	private static BigDecimal segundoIntervalo(BigDecimal valor)
			throws BusinessException {

		// se o valor for < 2 ou >= 6 o mesmo está incorreto.
		if ((valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[0])) < 0)
				|| (valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[1])) >= 0)) {
			throw new BusinessException();
		}

		BigDecimal valorExp4 = new BigDecimal(Math.pow(valor.doubleValue(), 4))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp3 = new BigDecimal(Math.pow(valor.doubleValue(), 3))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp2 = new BigDecimal(Math.pow(valor.doubleValue(), 2))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);

		// SE(B9<6;-0,291666666667652*B9^4 + 5,75000000000546*B9^3 -
		// 34,7083333333449*B9^2 + 85,2500000001091*B9 - 71,0000000003465;
		// se for menor que 6
		BigDecimal retorno = new BigDecimal("0.291666666667652")
				.multiply(valorExp4)
				.negate()
				// -0,291666666667652*B9^4
				.add(new BigDecimal("5.75000000000546").multiply(valorExp3))
				// + 5,75000000000546*B9^3
				.subtract(
						new BigDecimal("34.7083333333449").multiply(valorExp2))
				// - 34,7083333333449*B9^2
				.add(new BigDecimal("85.2500000001091").multiply(valor))
				// + 85,2500000001091*B9
				.subtract(new BigDecimal("71.0000000003465")); // -
																// 71.0000000003465

		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Utilizado para fazer o cálculo do Ph caso o valor informado é maior ou
	 * igual a 6 e menor que 9. Obs: este método não considera o valor 9, o
	 * valor deve ser menor que 9.
	 * 
	 * Cálculo realizado: -0,858313448072295*[valor]^6 +
	 * 37,530249732954*[valor]^5 - 680,545427632809*[valor]^4 +
	 * 6551,40354862855*[valor]^3 - 35335,6700858718*[valor]^2 +
	 * 101370,53913819*B9 - 120990,176842687;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto.
	 */
	private static BigDecimal terceiroIntervalo(BigDecimal valor)
			throws BusinessException {

		// se o valor nao estiver no intervalo correto.
		if ((valor.compareTo(new BigDecimal(TERCEIRO_INTERVALO[0])) < 0)
				|| (valor.compareTo(new BigDecimal(TERCEIRO_INTERVALO[1])) >= 0)) {
			throw new BusinessException();
		}

		BigDecimal valorExp4 = new BigDecimal(Math.pow(valor.doubleValue(), 4))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp3 = new BigDecimal(Math.pow(valor.doubleValue(), 3))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp2 = new BigDecimal(Math.pow(valor.doubleValue(), 2))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp6 = new BigDecimal(Math.pow(valor.doubleValue(), 6))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp5 = new BigDecimal(Math.pow(valor.doubleValue(), 5))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);

		// SE(B9<9; -0,858313448072295*B9^6 + 37,530249732954*B9^5 -
		// 680,545427632809*B9^4 + 6551,40354862855*B9^3 - 35335,6700858718*B9^2
		// + 101370,53913819*B9 - 120990,176842687;
		BigDecimal retorno = new BigDecimal("0.858313448072295")
				.multiply(valorExp6)
				.negate()
				// -0,858313448072295*B9^6
				.add(new BigDecimal("37.530249732954").multiply(valorExp5))
				// 37,530249732954*B9^5
				.subtract(
						new BigDecimal("680.545427632809").multiply(valorExp4))
				// - 680,545427632809*B9^4
				.add(new BigDecimal("6551.40354862855").multiply(valorExp3))
				// 6551,40354862855*B9^3
				.subtract(
						new BigDecimal("35335.6700858718").multiply(valorExp2))
				// - 35335,6700858718*B9^2
				.add(new BigDecimal("101370.53913819").multiply(valor))
				// 101370,53913819*B9
				.subtract(new BigDecimal("120990.176842687")); // -
																// 120990,176842687

		return retorno.setScale(ESCALA, BigDecimal.ROUND_DOWN);
	}

	/**
	 * Utilizado para fazer o cálculo do Ph caso o valor informado é maior ou
	 * igual a 9 e menor que 12.
	 * 
	 * Cálculo realizado: -0,666666666666671*[valor]^3 +
	 * 27,0000000000001*[valor]^2 - 361,333333333334*[valor] + 1602
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto.
	 */
	private static BigDecimal quartoIntervalo(BigDecimal valor)
			throws BusinessException {

		if ((valor.compareTo(new BigDecimal(QUARTO_INTERVALO[0])) < 0)
				|| (valor.compareTo(new BigDecimal(QUARTO_INTERVALO[1])) > 0)) {
			throw new BusinessException();
		}

		BigDecimal valorExp3 = new BigDecimal(Math.pow(valor.doubleValue(), 3))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp2 = new BigDecimal(Math.pow(valor.doubleValue(), 2))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);

		// SE(B9<12; -0,666666666666671*B9^3 + 27,0000000000001*B9^2 -
		// 361,333333333334*B9 + 1602;
		BigDecimal retorno = new BigDecimal("0.666666666666671")
				.multiply(valorExp3).negate() // -0,666666666666671*B9^3
				.add(new BigDecimal("27.0000000000001").multiply(valorExp2))
				// 27,0000000000001*B9^2
				.subtract(new BigDecimal("361.333333333334").multiply(valor))
				// - 361,333333333334*B9
				.add(new BigDecimal(1602)); // + 1602

		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Calcula o IQA do Ph informado. Cálculo realizado: Se [valor] <= que 0
	 * Lança exceção Se [valor] < 2 IQA = 0 Se [valor] < 6
	 * 
	 * @see {@link CalculoPhBusiness#segundoIntervalo(double)} Se [valor] < 9
	 * @see {@link CalculoPhBusiness#terceiroIntervalo(double)} Se [valor] <= 12
	 * @see {@link CalculoPhBusiness#quartoIntervalo(double)} Se [valor] > 12
	 *      IQA = 0
	 * 
	 * @param valor
	 *            Valor do Ph.
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido (Ex: menor que
	 *             0).
	 */
	public static BigDecimal calculaIQA(BigDecimal valor)
			throws BusinessException {

		// se o valor for menor do que 0 retorna erro
		if (valor.compareTo(BigDecimal.ZERO) < 0) {
			throw new BusinessException();
		}

		// se o valor for menor do que 2 retorna 0;
		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) < 0) {
			return BigDecimal.ZERO;
		}

		if (valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[1])) < 0) {
			return segundoIntervalo(valor);
		}

		if (valor.compareTo(new BigDecimal(TERCEIRO_INTERVALO[1])) < 0) {
			return terceiroIntervalo(valor);
		}

		if (valor.compareTo(new BigDecimal(QUARTO_INTERVALO[1])) <= 0) {
			return quartoIntervalo(valor);
		}

		// SE(B9>12; 0)
		// se for maior que 12 retorna 0
		return BigDecimal.ZERO;
	}

	/**
	 * Calcula a média de qualidade (qi^w) do Ph.
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido.
	 */
	protected static BigDecimal mediaQualidade(BigDecimal valor)
			throws BusinessException {
		return new BigDecimal(Math.pow(calculaIQA(valor).doubleValue(), PESO));
	}

}
